#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zone.h"


/*
==============================================================================

						ZONE MEMORY ALLOCATION

just cleared malloc with counters now...

==============================================================================
*/

#define	Z_MAGIC		0x1d1d
#define BAD_MAGIC	0xffff
#define FAILED_ALLOC 0xfffe


/*
========================
Z_Free
========================
*/
void Z_Free(zone_t* self, void *ptr)
{
	zhead_t	*z;

	z = ((zhead_t *)ptr) - 1;

	if (z->magic != Z_MAGIC)
		exit(BAD_MAGIC);

	z->prev->next = z->next;
	z->next->prev = z->prev;

	self->z_count--;
	self->z_bytes -= z->size;
	free (z);
}


/*
========================
Z_Stats_f
========================
*/
void Z_Stats_f(zone_t* self)
{
	printf("%i bytes in %i blocks\n", self->z_bytes, self->z_count);
}

/*
========================
Z_FreeTags
========================
*/
void Z_FreeTags (zone_t* self, int tag)
{
	zhead_t	*z, *next;

	for (z=self->z_chain->next ; z != self->z_chain ; z=next)
	{
		next = z->next;
		if (z->tag == tag)
			Z_Free (self, (void *)(z+1));
	}
}

/*
========================
Z_TagMalloc
========================
*/
void *Z_TagMalloc (zone_t* self, int size, int tag)
{
	zhead_t	*z;
	
	size = size + sizeof(zhead_t);
	z = malloc(size);
	if (!z)
		exit(FAILED_ALLOC);
	memset (z, 0, size);
	self->z_count++;
	self->z_bytes += size;
	z->magic = Z_MAGIC;
	z->tag = tag;
	z->size = size;

	z->next = self->z_chain->next;
	z->prev = self->z_chain;
	self->z_chain->next->prev = z;
	self->z_chain->next = z;

	return (void *)(z+1);
}

/*
========================
Z_Malloc
========================
*/
void *Z_Malloc (zone_t* self, int size)
{
	return Z_TagMalloc (self, size, 0);
}


/* constructor */
zone_t* Init_Zone(void)
{
	zone_t* zone = (zone_t*)malloc(sizeof(zone_t));
	zhead_t* z_chain = (zhead_t*)malloc(sizeof(zhead_t));
	z_chain->next = z_chain->prev = z_chain;
	zone->z_chain = z_chain;
	zone->z_count = zone->z_bytes = 0;
	zone->alloc = Z_Malloc;
	zone->dealloc = Z_Free;
	zone->status = Z_Stats_f;
	zone->tag_alloc = Z_TagMalloc;
	zone->tag_dealloc = Z_FreeTags;

	return zone;
}

/* destructor */
void Destroy_Zone(zone_t* self)
{
	free(self->z_chain);
	free(self);
}

/* get the singleton pointer */
zone_t* Get_Global_Zone(void)
{
	if (NULL == global_zone)
		global_zone = Init_Zone();
	
	return global_zone;
}

/* drop the singleton pointer */
void Drop_Global_Zone(void)
{
	if (NULL != global_zone)
	{
		Destroy_Zone(global_zone);
		global_zone = NULL;
	}
}
